<template>
  <view
    :id="id"
    class="tn-slider"
    :class="[sliderClass]"
    :style="sliderStyle"
    @tap.stop="handleSliderClick"
  >
    <!-- 选中时的滑动条 -->
    <view
      class="tn-slider__active"
      :class="[activeSliderClass]"
      :style="activeSliderStyle"
    ></view>

    <!-- 滑块 -->
    <view
     class="tn-slider__bar tn-slider__bar--default"
     :style="barStyle('default')"
     @touchstart.stop="barTouchStart"
     @touchmove.stop.prevent="barTouchMove($event,'default')"
     @touchend.stop="barTouchEnd"
    ></view>
    <view
      v-if="rangeMode"
     class="tn-slider__bar tn-slider__bar--range"
     :style="barStyle('range')"
     @touchstart.stop="barTouchStart"
     @touchmove.stop.prevent="barTouchMove($event,'range')"
     @touchend.stop="barTouchEnd"
    ></view>
  </view>
</template>

<script lang="ts" setup>
import { sliderProps, sliderEmits } from '../../../types/tn-slider'
import { computed, CSSProperties, ref, watch, getCurrentInstance, ComponentInternalInstance, onMounted, nextTick } from 'vue'
import { ColorInfo } from '../../../types/tuniao-ui'
import useComponentColor from '../../../libs/hooks/useComponentColor'
import { handleValueWithUnit, uuid, getRectInfo } from '../../../utils'
import { UPDATE_MODEL_EVENT } from '../../../constants'

type BarType = 'default' | 'range'

const props = defineProps(sliderProps)
const emits = defineEmits(sliderEmits)

// 解析颜色值
const { splitColorInfo } = useComponentColor()

// 解析选中时颜色
const activeColor = computed<ColorInfo>(() => {
  return splitColorInfo('bg', props?.activeColor || '')
})
// 解析未选中时颜色
const inactiveColor = computed<ColorInfo>(() => {
  return splitColorInfo('bg', props?.inactiveColor || '')
})

// 滑动条样式
const sliderStyle = computed<CSSProperties>(() => {
  let style: CSSProperties = {}
  if (props?.height) style.height = handleValueWithUnit(props.height)
  if (inactiveColor.value.type === 'style') style.backgroundColor = inactiveColor.value.value
  if (props?.barSize) {
    style.width = `calc(100% - ${handleValueWithUnit(props.barSize)})`
    style.margin = `0rpx calc(${handleValueWithUnit(props.barSize)} / 2)`
  }
  return style
})
// 被选中的滑动条样式
const activeSliderStyle = computed<CSSProperties>(() => {
  let style: CSSProperties = {}
  if (activeColor.value.type === 'style') style.backgroundColor = activeColor.value.value
  // 设置被选中滑动条的样式
  let leftValue: number = 0
  let rightValue: number = 0
  if (rangeMode.value) {
    leftValue = (selectValue.value as number[])[0]
    rightValue = (selectValue.value as number[])[1]
  } else {
    leftValue = 0
    rightValue = selectValue.value as number
  }
  const leftPositionValue = (leftValue <= min.value ? 0 : (leftValue - min.value) / (max.value - min.value)) * 100
  const rightPositionValue = 100 - ((rightValue <= min.value ? 0 : (rightValue - min.value) / (max.value - min.value)) * 100)
  style.left = `${leftPositionValue}%`
  style.right= `${rightPositionValue}%`
  return style
})
// 滑块样式
const barStyle = computed<(type: BarType) => CSSProperties>(() => {
  return (type: BarType) => {
    let style: CSSProperties = {}
    if (props?.barSize) {
      style.width = handleValueWithUnit(props.barSize)
      style.height = style.width
    }
    let value: number = min.value
    if (rangeMode.value) {
      if (type === 'default') {
        value = (selectValue.value as number[])[0]
      } else if (type === 'range') {
        value = (selectValue.value as number[])[1]
      }
    } else {
      if (type === 'default') {
        value = selectValue.value as number
      }
    }
    const leftPositionValue = value <= min.value ? 0 : (value - min.value) / (max.value - min.value)
    style.left = `${leftPositionValue * 100}%`
    return style
  }
})

// 滑动条对应的样式
const sliderClass = computed<string>(() => {
  let clazz: string[] = []
  if (props?.disabled) clazz.push('disabled')
  if (inactiveColor.value.type === 'class') clazz.push(inactiveColor.value.value)
  return clazz.join(' ')
})
// 被选中滑动条对应的样式
const activeSliderClass = computed<string>(() => {
  let clazz: string[] = []
  if (activeColor.value.type === 'class') clazz.push(activeColor.value.value)
  return clazz.join(' ')
})

// 最小值
let min = computed<number>(() => {
  return Number(props.min) || 0
})
// 最大值
let max = computed<number>(() => {
  return Number(props.max) || 100
})
// 步进值
let step = computed<number>(() => {
  return Number(props.step) || 1
})
// range模式
let rangeMode = ref<boolean>(false)
// 选中的值
let innerUpdate: boolean = false
let selectValue = ref<number | number[]>(0)

const instance = getCurrentInstance() as ComponentInternalInstance
const id: string = uuid()
// 获取滑动条的Rect数据
let sliderRectInitCount: number = 0
let sliderRectInfo: { left: number, right: number, width: number} = {
  left: 0,
  right: 0,
  width: 0
}
const initSliderRect = async () => {
  const rect = await getRectInfo(`#${id}`, instance)
  if (!rect) {
    if (sliderRectInitCount > 10) {
      console.error('[tn-slider]获取滑动条节点信息失败')
      return
    }
    setTimeout(() => {
      initSliderRect()
      sliderRectInitCount++
    }, 50)
    return
  }

  sliderRectInitCount = 0
  sliderRectInfo.left = rect.left || 0
  sliderRectInfo.right = rect.right || 0
  sliderRectInfo.width = rect.width || 0
}

// 触摸状态标记
let touchState: 'touch' | 'moving' | 'end'
// 处理滑块的滑动事件
const barTouchStart = (e: TouchEvent) => {
  if(props?.disabled || !e.changedTouches[0]) return
  touchState = 'touch'
}
const barTouchMove = (e: TouchEvent, type: BarType) => {
  if(props?.disabled || !e.changedTouches[0]) return

  // 计算当前滑动的位置在滑动条的相对位置
  let touchX = e.changedTouches[0].pageX
  const value = calcValue(touchX)
  if (rangeMode.value) {
    let tempValue: number[] = selectValue.value as number[]
    if (type === 'default') {
      tempValue[0] = value
      if (tempValue[0] >= tempValue[1]) tempValue[0] = tempValue[1]
    } else if (type === 'range') {
      tempValue[1] = value
      if (tempValue[0] >= tempValue[1]) tempValue[1] = tempValue[0]
    }
    updateSliderValue(tempValue)
  } else {
    updateSliderValue(value)
  }
  touchState = 'moving'
}
const barTouchEnd = (e: TouchEvent) => {
  if(props?.disabled || !e.changedTouches[0] || touchState !== 'moving') return
  touchState = 'end'
}

// 滑动条点击事件
const handleSliderClick = (e: TouchEvent) => {
  if(props?.disabled || !e.changedTouches[0]) return
  // 计算当前滑动的位置在滑动条的相对位置
  let touchX = e.changedTouches[0].pageX
  if (rangeMode.value) {
    let tempValue: number[] = selectValue.value as number[]
    // 计算两个滑块的位置
    const defaultPosition = ((tempValue[0] - min.value) / (max.value - min.value) * sliderRectInfo.width) + sliderRectInfo.left
    const rangePosition = ((tempValue[1] - min.value) / (max.value - min.value) * sliderRectInfo.width) + sliderRectInfo.left
    console.log(defaultPosition, rangePosition, touchX);
    if (Math.abs(defaultPosition - touchX) <= Math.abs(rangePosition - touchX)) {
      tempValue[0] = calcValue(touchX)
    } else {
      tempValue[1] = calcValue(touchX)
    }
    updateSliderValue(tempValue)
  } else {
    updateSliderValue(calcValue(touchX))
  }
  
}
// 计算value的数值
const calcValue = (touchX: number): number => {
  // 触摸距离不能超过滑动条的左右位置
  if (touchX < sliderRectInfo.left) touchX = sliderRectInfo.left
  if (touchX > sliderRectInfo.right) touchX = sliderRectInfo.right
  const distance: number = touchX - sliderRectInfo.left
  const position: number = distance / sliderRectInfo.width
  // 计算当前位置对应的值
  const value = Math.round(((max.value - min.value) * position) + min.value)
  return value
}

// 更新滑动的数值
const updateSliderValue = (value: number | number[]) => {
  if (rangeMode.value) {
    value = (value as number[]).map(item => {
      return Math.round(item / step.value) * step.value
    })
  } else {
    value = Math.round((value as number) / step.value) * step.value
  }
  emits(UPDATE_MODEL_EVENT, value)
  emits('change', value)
}
watch(() => props.modelValue, (
  value: number | Array<any>,
  oValue: number | Array<any> | undefined
) => {
  if (innerUpdate) {
    innerUpdate = false
    return
  }
  if (Array.isArray(value)) {
    rangeMode.value = true
    let tempValue: number[] = Array(2).fill(0)
    value.map((item, index) => {
      if (index === 0) {
        tempValue[0] = !item ? min.value : (item < min.value ? min.value : item)
      } else if (index === 1) {
        tempValue[1] = !item ? max.value : (item > max.value ? max.value : item)
      }
    })
    selectValue.value = tempValue
  } else {
    rangeMode.value = false
    if (value < min.value || !value) selectValue.value = min.value
    else if (value > max.value) selectValue.value = max.value
    else selectValue.value = value
  }
  if (oValue === undefined) {
    innerUpdate = true
    updateSliderValue(selectValue.value)
  }
}, {
  immediate: true
})

onMounted(() => {
  nextTick(() => {
    setTimeout(() => {
      initSliderRect()
    }, 50);
  })
})
</script>

<script lang="ts">
export default {
  options: {
    // 在微信小程序中将组件节点渲染为虚拟节点，更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
    virtualHost: true
  }
}
</script>

<style lang="scss" scoped>
@import '../../../theme.scss';

.tn-slider {
  position: relative;
  width: calc(100% - 35rpx);
  height: 8rpx;
  background-color: #E6E6E6;
  border-radius: 10rpx;
  margin: 0rpx calc(40rpx / 2);
  touch-action: none;

  &__active {
    background-color: $tn-main-color;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0%;
    right: 0%;
    border-radius: 10rpx;
  }

  &__bar {
    position: absolute;
    width: 30rpx;
    height: 30rpx;
    background-color: #FFFFFF;
    border-radius: 50%;
    box-shadow: 1px 1px 9px 2px rgba(0, 0, 0, 0.1);
    top: 50%;
    transform: translate(-50%, -50%);

    &--default {
      left: 0%;
      z-index: 1;
    }
    &--range {
      left: 0%;
      z-index: 2;
    }
  }
}
</style>
